home *** CD-ROM | disk | FTP | other *** search
/ The Atari Compendium / The Atari Compendium (Toad Computers) (1994).iso / files / prgtools / mint / utilit~1 / stelm3.lzh / STELM / TERMCAP.C < prev   
Encoding:
C/C++ Source or Header  |  1993-11-11  |  22.4 KB  |  872 lines

  1. /************************************************************************
  2.  *                                    *
  3.  *            Copyright (c) 1982, Fred Fish            *
  4.  *                All Rights Reserved                *
  5.  *                                    *
  6.  *    This software and/or documentation is released for public    *
  7.  *    distribution for personal, non-commercial use only.        *
  8.  *    Limited rights to use, modify, and redistribute are hereby    *
  9.  *    granted for non-commercial purposes, provided that all        *
  10.  *    copyright notices remain intact and all changes are clearly    *
  11.  *    documented.  The author makes no warranty of any kind with    *
  12.  *    respect to this product and explicitly disclaims any implied    *
  13.  *    warranties of merchantability or fitness for any particular    *
  14.  *    purpose.                            *
  15.  *                                    *
  16.  ************************************************************************
  17.  */
  18.  
  19.  
  20. /*
  21.  *    tgetflag   extract boolean termcap capability
  22.  *
  23.  *    Returns TRUE if specified id is present in terminal
  24.  *    entry, FALSE otherwise.
  25.  *
  26.  */
  27.  
  28. #include <stdio.h>
  29. #include <string.h>
  30. #include <termcap.h>
  31. #include <ctype.h>
  32.  
  33. #define TRUE 1
  34. #define FALSE 0
  35. #define BUFSIZE 1024            /* Assumed size of external buffer */
  36.  
  37. #define NO_FILE     -1            /* Returned if can't open file */
  38. #define NO_ENTRY  0            /* Returned if can't find entry */
  39. #define SUCCESS   1            /* Returned if entry found ok */
  40. #define TRUNCATED 2            /* Returned if entry found but trunc */
  41.  
  42. # define DEFAULT_ROOT "termcap"        /* name without path component */
  43. # ifdef __MINT__
  44. # define DEFAULT_FILE "u:\\etc\\termcap"
  45. # else
  46. # define DEFAULT_FILE "/etc/termcap"    /* default termcap filename */
  47. # endif
  48.  
  49. char *_tcpbuf;        /* Termcap entry buffer pointer */
  50.  
  51. int tgetflag(char *id)
  52. {
  53.     char *bp;
  54.  
  55.     bp = _tcpbuf;
  56.  
  57.     while ((bp = strchr(bp, ':')) != NULL) {
  58.     bp++;
  59.     if (*bp++ == id[0] && *bp != '\0' && *bp++ == id[1]) {
  60.         if (*bp == '\0' || *bp++ == ':') {
  61.         return(TRUE);
  62.         } else {
  63.         return(FALSE);
  64.         }
  65.     }
  66.     }
  67.     return(FALSE);
  68. }
  69.  
  70. /*
  71.  *    do_esc    process an escaped sequence
  72.  *
  73.  *    Processes an escape sequence pointed to by
  74.  *    in, transfering it to location pointed to
  75.  *    by out, and updating the pointer to in.
  76.  *
  77.  */
  78.  
  79. static char *maplist = {"E\033b\bf\fn\nr\rt\t"};
  80.  
  81. static char *do_esc(char *out,char *in)
  82. {   int count;
  83.     char ch;
  84.     char *cp;
  85.  
  86.     if (*in != '\0') {
  87.     if (isdigit(*in)) {
  88.         ch = 0;
  89.         for (count = 0; count < 3 && isdigit(*in); in++) {
  90.          ch <<= 3;
  91.          ch |= (*in - '0');
  92.         }
  93.         *out++ = ch;
  94.     } else if ((cp = strchr(maplist,*in)) != NULL) {
  95.         *out++ = *++cp;
  96.         in++;
  97.     } else {
  98.         *out++ = *in++;
  99.     }
  100.     }
  101.     return(in);
  102. }
  103.  
  104. /*
  105.  *
  106.  *    decode   transfer string capability, decoding escapes
  107.  *
  108.  *  DESCRIPTION
  109.  *
  110.  *    Transfers the string capability, up to the next ':'
  111.  *    character, or null, to the buffer pointed to by
  112.  *    the pointer in *area.  Note that the initial
  113.  *    value of *area and *area is updated to point
  114.  *    to the next available location after the null
  115.  *    terminating the transfered string.
  116.  *
  117.  *  BUGS
  118.  *
  119.  *    There is no overflow checking done on the destination
  120.  *    buffer, so it better be large enough to hold
  121.  *    all expected strings.
  122.  *
  123.  */
  124.  
  125. static char *decode(char *bp,char **area)
  126. {   char *cp, *bgn;
  127.  
  128.     cp = *area;
  129.     while (*bp != '\0' && *bp != ':') {
  130.     switch(*bp) {
  131.     case '\\':
  132.         bp = do_esc(cp++,++bp);
  133.         break;
  134.     case '^':
  135.         *cp++ = *++bp & 037;
  136.         bp++;
  137.         break;
  138.     default:
  139.         *cp++ = *bp++;
  140.         break;
  141.     }
  142.     }
  143.     *cp++ = '\0';
  144.     bgn = *area;
  145.     *area = cp;
  146.     return(bgn);
  147. }
  148.  
  149. /*
  150.  *
  151.  *    tgetstr   extract string capability from termcap entry
  152.  *
  153.  *    Gets the string capability for <id>, placing it in
  154.  *    the buffer at *area, and advancing *area to point
  155.  *    to next available storage.
  156.  *
  157.  *    For example, if the following capabilities are
  158.  *    in the termcap file:
  159.  *
  160.  *        ZZ=zzzz
  161.  *        YY=yyyyyy
  162.  *        WW=www
  163.  *
  164.  *    then successive calls using YY, ZZ, and WW will
  165.  *    build the following buffer:
  166.  *
  167.  *        yyyyyy0zzzz0www0
  168.  *
  169.  *    The first call will return a pointer to yyyyyy, the
  170.  *    second will return a pointer to zzzz and the third
  171.  *    will return a pointer to www.  Note that each
  172.  *    string is null terminated, as are all C strings.
  173.  *
  174.  *    Characters preceded by the carot character (\136)
  175.  *    are mapped into the corresponding control character.
  176.  *    For example, the two character sequence ^A becomes
  177.  *    a single control-A (\001) character.
  178.  *
  179.  *    The escape character is the normal C backslash and
  180.  *    the normal C escape sequences are recognized, along
  181.  *    with a special sequence for the ASCII escape character
  182.  *    (\033).  The recognized sequences are:
  183.  *
  184.  *        \E   =>  '\033'  (ASCII escape character)
  185.  *        \b   =>  '\010'  (ASCII backspace character)
  186.  *        \f   =>  '\014'  (ASCII form feed character)
  187.  *        \n   =>  '\012'  (ASCII newline/linefeed char)
  188.  *        \r   =>  '\015'  (ASCII carriage return char)
  189.  *        \t   =>  '\011'  (ASCII tab character)
  190.  *        \ddd =>  '\ddd'  (arbitrary ASCII digit)
  191.  *        \x   =>  'x'     (ordinary ASCII character)
  192.  *
  193.  */
  194.  
  195. char *tgetstr(char *id,char **area)
  196. {
  197.     char *bp;
  198.  
  199.     bp = _tcpbuf;
  200.     while ((bp = strchr(bp,':')) != NULL) {
  201.     bp++;
  202.     if (*bp++ == id[0] && *bp != '\0' && *bp++ == id[1]) {
  203.         if (*bp != '\0' && *bp++ != '=') {
  204.         return(NULL);
  205.         } else {
  206.         return(decode(bp,area));
  207.         }
  208.     }
  209.     }
  210.     return(NULL);
  211. }
  212.  
  213. /*
  214.  *
  215.  *    tputs     output string with appropriate padding
  216.  *
  217.  *    Outputs string pointed to by cp, using function outc, and
  218.  *    following it with the appropriate number of padding characters.
  219.  *    Affcnt contains the number of lines affected, which is used
  220.  *    as a multiplier for the specified per line pad time.  If
  221.  *    per line pad count is not applicable, affcnt should be 1,
  222.  *    NOT zero.
  223.  *
  224.  *    The format of the string pointed to by cp is:
  225.  *
  226.  *        [pad time][*]<string to send>
  227.  *
  228.  *        where:    pad time => time to delay in milliseconds
  229.  *            * => specifies that time is per line
  230.  *            
  231.  *    The pad character is assumed to reside in the external
  232.  *    variable "PC".  Also, the external variable "ospeed"
  233.  *    should contain the output speed of the terminal as
  234.  *    encoded in /usr/include/sgtty.h  (B0-B9600).
  235.  *
  236.  *  BUGS
  237.  *
  238.  *    Digit conversion is based on native character set
  239.  *    being ASCII.
  240.  *
  241.  */
  242.  
  243. # ifdef SGTTY
  244. extern char PC;            /* Pad character to use */
  245. extern char ospeed;        /* Encoding of output speed */
  246.  
  247. static int _times[] = {
  248.     0,                /* Tenths of ms per char 0 baud */
  249.     2000,            /* Tenths of ms per char 50 baud */
  250.     1333,            /* Tenths of ms per char 75 baud */
  251.     909,            /* Tenths of ms per char 110 baud */
  252.     743,            /* Tenths of ms per char 134 baud */
  253.     666,            /* Tenths of ms per char 150 baud */
  254.     500,            /* Tenths of ms per char 200 baud */
  255.     333,            /* Tenths of ms per char 300 baud */
  256.     166,            /* Tenths of ms per char 600 baud */
  257.     83,                /* Tenths of ms per char 1200 baud */
  258.     55,                /* Tenths of ms per char 1800 baud */
  259.     41,                /* Tenths of ms per char 2400 baud */
  260.     20,                /* Tenths of ms per char 4800 baud */
  261.     10                /* Tenths of ms per char 9600 baud */
  262. };
  263. # endif
  264.  
  265. # ifdef SGTTY
  266. static void do_padding (int ptime, int (*outc )(int));
  267. # endif
  268.  
  269. void tputs(char *cp,int affcnt,int (*outc) (int))
  270. {
  271.     int ptime;            /* Pad time in tenths of milliseconds */
  272.  
  273.     if (cp == NULL || *cp == '\0') {
  274.     return;
  275.     } else {
  276.     for (ptime = 0; isdigit(*cp); cp++) {
  277.         ptime *= 10;
  278.         ptime += (*cp - '0');
  279.     }
  280.     ptime *= 10;
  281.     if (*cp == '.') {
  282.         cp++;
  283.         if (isdigit(*cp)) {
  284.         ptime += (*cp++ - '0');
  285.         }
  286.         while (isdigit(*cp)) {cp++;}
  287.     }
  288.     if (*cp == '*') {
  289.         ptime *= affcnt;
  290.         cp++;
  291.     }
  292.     while (*cp != '\0') {
  293.         (*outc)(*cp++);
  294.     }
  295. # ifdef SGTTY
  296.     do_padding(ptime,outc);
  297. # endif
  298.     }
  299. }
  300.  
  301. # ifdef SGTTY
  302. /*
  303.  *    do_padding    transmit any pad characters required
  304.  *
  305.  *    Does any padding required as specified by ptime (in tenths
  306.  *    of milliseconds), the output speed given in the external
  307.  *    variable ospeed, and the pad character given in the
  308.  *    external variable PC.
  309.  *
  310.  */
  311.  
  312.  
  313. static void do_padding(ptime,outc)
  314. int ptime;
  315. int (*outc) (int);
  316. {
  317.     register int nchars;
  318.     register int tpc;
  319.  
  320.     if (ptime != 0) {
  321.     if (ospeed >= 0 && ospeed <= (sizeof(_times)/ sizeof(int))) {
  322.         tpc = _times[ospeed];
  323.         ptime += (tpc / 2);
  324.         nchars = ptime / tpc;
  325.         for ( ; nchars > 0; --nchars) {
  326.         (*outc)(PC);
  327.         }
  328.     }
  329.     }
  330. }
  331. # endif
  332.  
  333. /*
  334.  *    build_entry    construct termcap entry in a given buffer
  335.  *
  336.  *      For a given name build in a buffer bp a termcap description
  337.  *      using a contents of file fp.  Continue this until the entry
  338.  *      is complete or we reached stop.  Concatenate entries if
  339.  *      required by tc capability.   White space characters and
  340.  *      backslashes escaping newlines are not copied into bp.
  341.  *      Returns SUCCESS if there was no problems, NO_ENTRY if an
  342.  *      entry with given name was not found and TRUNCATED if we
  343.  *      run out of a buffer space or continuation entry specified
  344.  *      with tc was not found
  345.  *
  346.  *  BUGS
  347.  *
  348.  *      Termcap specifications require for tc to be the last capability
  349.  *      for the given name.  This is not enforced but anything which
  350.  *      follows tc in the same description will be discarded.
  351.  *      It is not entirely clear what we should return when continuation
  352.  *      specified with tc failed.
  353.  *      Other stuff which goes beyond termcap specs can be accepted.
  354.  *      Terminal names starting with '#' are not accepted.
  355.  *      Continuation with names over 127 characters long will likely bomb!
  356.  *
  357.  *  AUTHOR
  358.  *
  359.  *    Michal Jaegermann
  360.  *
  361.  */
  362.  
  363. static int build_entry(char *bp,char *stop,FILE *fp, char *name)
  364. {
  365.     int c;
  366.     int so_far, skip_all = 0;
  367.     char *np;
  368.     char nbuf[128];
  369.     extern int _tgetc (FILE *fp);
  370.  
  371.     /* rewind file - we may seek for a continuation entry */
  372.     rewind(fp);
  373.  
  374.     /*
  375.      * this is FSM - sort of 
  376.      */
  377.     while (EOF != (c = getc(fp))) {
  378.      /*
  379.       * we are looking at a comment - skip it
  380.       */
  381.     if ('#' == c) {
  382.         do {
  383.         if (EOF == (c = getc(fp)))
  384.             return NO_ENTRY;
  385.         } while ('\n' != c);
  386.     }
  387.     /*
  388.      * empty line or we finished comment traversal;
  389.      * a little bit to good - but valid termcap file will be
  390.      * stil accepted
  391.      */
  392.     if (isspace(c))
  393.         continue;
  394.     /*
  395.      * try matching name
  396.      */
  397.     np = name;
  398.     while (*np == c) {
  399.         np += 1;
  400.         c = _tgetc(fp);
  401.     }
  402.     /*
  403.      * we finished traversing our name - is this really a match ?
  404.      */
  405.     if (*np == '\0') {
  406.         if (c == '|' || c == ':')
  407.         break; /* we have a match */
  408.         if  (c == EOF)
  409.         return (TRUNCATED); /* match - but we wanted more */
  410.     }
  411.     /*
  412.      * no match - skip until next name or next entry
  413.      * if we are past all possible names here 
  414.      */
  415.     skip_all = 0;
  416.     while ('\n' != c) {
  417.         if (':' == c)
  418.         skip_all = 1; /* we are past all valid names for this entry */
  419.         if ('|' == c && 0 == skip_all)
  420.         break;
  421.         c = _tgetc(fp);
  422.     }
  423.     }
  424.     if (EOF == c)
  425.     return (NO_ENTRY);
  426.     while (':' != c)    /* skip the remainig variants of terminal names */
  427.     c = _tgetc(fp); /* we do not want any mixups later             */
  428.  
  429.     /*
  430.      * at last we got it - copy terminal description into bp
  431.      */
  432.     so_far = 0;  /* rough indicator how far we are into a capability */
  433.     while ('\n' != (c = _tgetc(fp))) {
  434.     if (0 == so_far && !isalpha(c))
  435.         continue;     /* do not bother with all kind of spurious stuff */
  436.     so_far++;
  437.     if (1 == so_far && 't' == c ) {
  438.         /* a special case - maybe we have "tc=" string? */
  439.         if ((bp + 3) > stop) {
  440.         ungetc(c, fp);
  441.         continue;
  442.         /* cheating with so_far, but we want to skip this case! */
  443.         }
  444.         *bp++ = c;
  445.         c = _tgetc(fp);
  446.         if ('c' == c) {
  447.         *bp++ = c;
  448.         c = _tgetc(fp);
  449.         if ('=' == c) {
  450.             /* we will continue with a name which follows */
  451.             bp -= 2;
  452.             /* copy new name to nbuf */
  453.             np = nbuf;
  454.             while (':' != (c = _tgetc(fp))) {
  455.             if ('\n' == c || EOF == c)
  456.                 break;
  457.             *np++ = c;
  458.             }
  459.             *np = '\0';
  460.             return (SUCCESS == build_entry(bp, stop, fp, nbuf) ?
  461.                 SUCCESS : TRUNCATED);
  462.         }
  463.         }
  464.     }  /* end of 'tc=' check */
  465.     if (':' == c) /* literal colon cannot occur in capabilities strings -
  466.                * one has to use '\072' instead */
  467.         so_far =  0;
  468.     *bp++ = c;
  469.     if (bp >= stop)
  470.         return(TRUNCATED);
  471.     }
  472.     if (bp < stop)
  473.     *bp = '\0';
  474.     return(SUCCESS);
  475. }
  476.  
  477. /*
  478.  * Auxilary function to read a character from a text file
  479.  * with skipping escaped line terminators; any escaped
  480.  * '\n' will be replaced by a character which follows.
  481.  * After escape any number of ^M's will vanish,
  482.  * i.e a string of three characters '\\', 0x0d, 'a' will read
  483.  * as a string of two characters '\\', 'a' and so on...
  484.  * We do not tolerate such garbage in text files. :-)
  485.  */
  486.  
  487. int _tgetc(FILE *fp)
  488. {   int c;
  489.  
  490.     if ('\\' == (c = getc(fp))) {
  491.     while ('\r' == (c = getc(fp)))
  492.         ;                /* Messy stuff - go away */
  493.     if (c != '\n') {
  494.         ungetc(c, fp);
  495.         return ('\\');
  496.     }
  497.     c = getc(fp);
  498.     }
  499.     return(c);
  500. }
  501.  
  502. #ifdef MINT
  503. # define PATHSEP ','
  504. #else
  505. # define PATHSEP ';'
  506. #endif
  507.  
  508. /* Follow the PATH, trying to fopen the file.  Takes one additional
  509.  * argument which can be NULL.  Otherwise this argument gets filled
  510.  * in the full path to the file.  Returns as does fopen().
  511.  
  512.    Fopenp was already defined in stdio.h on the GCC compiler, so I
  513.    renamed it to nfopenp() (Kees Lemmens, 93)
  514.  */
  515.  
  516. static FILE *
  517. nfopenp(char *name,char *mode,char *pathname)
  518. {
  519.     char buffer[BUFSIZ], *buf, *bufp, *pathp, lastch;
  520.     FILE *fp;
  521.     extern char *getenv (const char *);
  522.  
  523.     /* If pathname is given, use it instead of buf so the calling
  524.      * process knows the path we found name under
  525.      */
  526.     if (pathname)
  527.         buf = pathname;
  528.     else
  529.         buf = buffer;
  530.  
  531.     strcpy(buf, name);
  532.     pathp = getenv("PATH");
  533.     while (pathp && *pathp) {
  534.         bufp = buf;
  535.         while (*pathp && *pathp != PATHSEP)
  536.             lastch = *bufp++ = *pathp++;
  537.         if (lastch != '\\')
  538.             *bufp++ = '\\';
  539.         strcpy(bufp, name);
  540.         if ((fp = fopen(buf, mode)) != NULL)
  541.             return fp;
  542.         if (*pathp)
  543.             pathp++;
  544.     }
  545.     return NULL;
  546. }
  547.  
  548. /*
  549.  * we do not really need the following part once the stuff is in
  550.  * our termcap  buffer
  551.  */
  552.  
  553. static char term_default[] = "st52:co#80:li#25\
  554. :al=\\EL:am:bs:cd=\\EJ:ce=\\EK:cl=\\EE:cm=\\EY%+ %+ :dl=\\EM\
  555. :do=\\EB:eo:ho=\\EH:is=\\Eq\\EE\\Ee\\Ev:it#8:pt:kb=^H:ll=\\EY9!\
  556. :me=\\Eq:mr=\\Ep:le=\\ED:nd=\\EC:rc=\\Ek:sc=\\Ej:se=\\Eq:so=\\Ep:ta=^I\
  557. :up=\\EA:ve=\\Ee:vi=\\Ef:km:bl=^G:cr=^M:ti=\\Ev\\Ee:sr=\\EI:sf=^J";
  558.  
  559. /*
  560.  *    find_file    find the termcap file and open it if possible
  561.  *
  562.  *    Attempts to locate and open the termcap file.  Also handles
  563.  *    using the environment TERMCAP string as the actual buffer
  564.  *    (that's why bp has to be an input parameter).
  565.  *
  566.  *    If TERMCAP is defined an begins with a '/' character then
  567.  *    it is taken to be the pathname of the termcap file and
  568.  *    an attempt is made to open it.  If this fails then
  569.  *    the default termcap file is used instead.
  570.  *
  571.  *    If TERMCAP is defined but does not begin with a '/' then
  572.  *    it is assumed to be the actual buffer contents provided
  573.  *    that <name> matches the environment variable TERM.
  574.  *
  575.  *  BUGS
  576.  *
  577.  *    There is currently no way to be sure which termcap
  578.  *    file was opened since the default will always be
  579.  *    tried.
  580.  *
  581.  */
  582.  
  583. static FILE *find_file(char *bp)
  584. {
  585.     FILE *fp;
  586.     char *cp;
  587.     extern char *getenv (const char *);
  588.  
  589.     if ((cp = getenv("TERMCAP")) != NULL) {
  590.     if (*cp != '\0') {
  591.         if (*cp == '/' || *cp == '\\' || (cp[1] == ':')) {
  592.         if ((fp = fopen(cp,"r")) != NULL) {
  593.             return(fp);
  594.         }
  595.         } else {
  596.         if (getenv("TERM") != NULL) {
  597.             strcpy(bp,cp);
  598.             return((FILE *)NULL);
  599.         }
  600.         }
  601.     }
  602.     }
  603.     /* Try current directory, then /etc/termcap, then along the path
  604.      */
  605.     if ((fp = fopen(DEFAULT_ROOT, "r")) != NULL)
  606.         return fp;
  607.     else if ((fp = fopen(DEFAULT_FILE, "r")) != NULL)
  608.         return fp;
  609.     else if ((fp = nfopenp(DEFAULT_ROOT, "r", NULL)) != NULL)
  610.         return fp;
  611.     else {
  612.         /* 
  613.          * if we do not have any better information, then
  614.          */
  615.         strcpy (bp, term_default);
  616.         return (FILE *) NULL;
  617.     }
  618. }
  619.  
  620. /*
  621.  *
  622.  *    tgetent   load buffer with entry for specified terminal
  623.  *
  624.  *    Extracts the entry for terminal <name> from the termcap file
  625.  *    and places it in the character buffer <bp>.   It is currently
  626.  *    assumed that bp is at least 1024 characters.  If the entry in
  627.  *    the termcap file is larger than 1023 characters the excess
  628.  *    characters will be discarded and appropriate status will
  629.  *    be returned.
  630.  *
  631.  *    Also note that since bp is used by other termcap
  632.  *    routines, the storage associated with the termcap entry
  633.  *    cannot be freed until all termcap calls are completed.
  634.  *
  635.  *    Tgetent can be directed to look in a file other than
  636.  *    the default (/etc/termcap) by defining an environment
  637.  *    variable called TERMCAP to be the pathname of the desired
  638.  *    termcap file.  This is useful for debugging new entries.
  639.  *    NOTE: the pathname MUST begin with a '/' character.
  640.  *      (Atari ST specific change: the pathname may begin with '\',
  641.  *       or with a drive letter followed by ':').
  642.  *
  643.  *    Also, if the string assigned to TERMCAP does not begin with
  644.  *    a '/' and if the environment variable TERM matches <name> then
  645.  *    the string assigned to TERMCAP is copied to buffer <bp> 
  646.  *    instead of reading a termcap file.
  647.  *      (Atari ST specific change: TERM is no longer checked (the
  648.  *       check was buggy).
  649.  *
  650.  *    Modification by ERS: if no termcap file can be found, then
  651.  *    a default termcap is used (this is for GEMDOS).
  652.  *
  653.  *      Further mods by MJ: original routines fail to proces valid
  654.  *      termcap files - replaced with new versions.
  655.  *      Atari specific: default termcap used when nothing better is
  656.  *      around reads a number of rows and colums from Line-A variables.
  657.  *    
  658.  *  RETURNS
  659.  *
  660.  *    -1  if the termcap file cannot be opened
  661.  *     0  if no entry in termcap file matches <name>
  662.  *     1  if extraction is successful with no errors
  663.  *     2  if extraction is successful but entry truncated
  664.  *
  665.  *  SEE ALSO
  666.  *
  667.  *    tgetnum   extract numeric type capability
  668.  *    tgetflag  test boolean type capability
  669.  *    tgetstr   get string value of capability
  670.  *
  671.  *  AUTHOR
  672.  *
  673.  *    Fred Fish
  674.  *
  675.  */
  676.  
  677. int tgetent(bp,name)
  678. char *bp;                /* Pointer to buffer (1024 char min) */
  679. char *name;                /* Pointer to terminal entry to find */
  680. {
  681.     FILE *fp;
  682.  
  683.     *bp = '\0';
  684.     _tcpbuf = bp;
  685.     if ((fp = find_file(bp)) == NULL) {
  686.     if (*bp != '\0') {
  687.         return(SUCCESS);
  688.     } else {
  689.         return(NO_FILE);
  690.     }
  691.     } else {
  692.     *bp++ = ':';
  693.     return (build_entry(bp, bp + BUFSIZE - 1, fp, name));
  694.     }
  695. }
  696.  
  697. /*
  698.  *
  699.  *    tgetnum   extract numeric option from termcap entry
  700.  *
  701.  *    Returns numeric value of capability <id>, or -1 if <id>
  702.  *    is not found.   Knows about octal numbers, which
  703.  *    begin with 0.
  704.  *
  705.  */
  706.  
  707. extern char *_tcpbuf;        /* Termcap entry buffer pointer */
  708.  
  709. int tgetnum(id)
  710. char *id;
  711. {
  712.     int value, base;
  713.     char *bp;
  714.  
  715.     bp = _tcpbuf;
  716.     while ((bp = strchr(bp,':')) != NULL) {
  717.     bp++;
  718.     if (*bp++ == id[0] && *bp != '\0' && *bp++ == id[1]) {
  719.         if (*bp != '\0' && *bp++ != '#') {
  720.         return(-1);
  721.         } else {
  722.         value = 0;
  723.         if (*bp == '0') {
  724.             base = 8;
  725.         } else {
  726.             base = 10;
  727.         }
  728.         while (isdigit(*bp)) {
  729.             value *= base;
  730.             value += (*bp++ - '0');
  731.         }
  732.         return(value);
  733.         }
  734.     }
  735.     }
  736.     return(-1);
  737. }
  738.  
  739. /*
  740.  *
  741.  *    tgoto   expand cursor addressing string from cm capability
  742.  *
  743.  *    Returns cursor addressing string, decoded from the cm
  744.  *    capability string, to move cursor to column destcol on
  745.  *    line destline.
  746.  *
  747.  *    The following sequences uses one input argument, either
  748.  *    line or column, and place the appropriate substitution
  749.  *    in the output string:
  750.  *
  751.  *        %d    substitute decimal value (in ASCII)
  752.  *        %2    like %d but forces field width to 2
  753.  *        %3    like %d but forces field width to 3
  754.  *        %.    like %c
  755.  *        %+x    like %c but adds ASCII value of x
  756.  *
  757.  *    The following sequences cause processing modifications
  758.  *    but do not "use up" one of the arguments.  If they
  759.  *    act on an argument they act on the next one to
  760.  *    be converted.
  761.  *
  762.  *        %>xy    if next value to be converted is
  763.  *            greater than value of ASCII char x
  764.  *            then add value of ASCII char y.
  765.  *        %r    reverse substitution of line
  766.  *            and column (line is substituted
  767.  *            first by default).
  768.  *        %i    causes input values destcol and
  769.  *            destline to be incremented.
  770.  *        %%    gives single % character in output.
  771.  *
  772.  *  BUGS
  773.  *
  774.  *    Does not implement some of the more arcane sequences for
  775.  *    radically weird terminals (specifically %n, %B, & %D).
  776.  *    If you have one of these you deserve whatever happens.
  777.  *
  778.  */
  779.  
  780. #define MAXARGS 2
  781.  
  782. static char *in;        /* Internal copy of input string pointer */
  783. static char *out;        /* Pointer to output array */
  784. static int args[MAXARGS];    /* Maximum number of args to convert */
  785. static int pcount;        /* Count of args processed */
  786. static char output[64];        /* Converted string */
  787. static void process (void);
  788.  
  789. char *tgoto(cm,destcol,destline)
  790. char *cm;
  791. int destcol;
  792. int destline;
  793. {
  794.     if (cm == NULL) {
  795.     return("");
  796.     } else {
  797.     in = cm;
  798.     out = output;
  799.     args[0] = destline;
  800.     args[1] = destcol;
  801.     pcount = 0;
  802.     while (*in != '\0') {
  803.         if (*in != '%') {
  804.         *out++ = *in++;
  805.         } else {
  806.         process();
  807.         }
  808.     }
  809.      *out = 0;
  810.     return(output);
  811.     }
  812. }
  813. /*
  814.  *    process   process the conversion/command sequence
  815.  *
  816.  *    Processes the sequence beginning with the % character.
  817.  *    Directly manipulates the input string pointer, the
  818.  *    output string pointer, and the arguments.  Leaves
  819.  *    the input string pointer pointing to the next character
  820.  *    to be processed, and the output string pointer pointing
  821.  *    to the next output location.  If conversion of
  822.  *    one of the numeric arguments occurs, then the pcount
  823.  *    is incremented.
  824.  *
  825.  */
  826.  
  827. static void process(void)
  828. {
  829.     int temp;
  830.  
  831.     in++;
  832.     switch(*in++) {
  833.     case 'd':
  834.     sprintf(out,"%d",args[pcount++]);
  835.     out = &output[(int)strlen(output)];    
  836.     break;
  837.     case '2':
  838.     sprintf(out,"%02d",args[pcount++]);
  839.     out = &output[(int)strlen(output)];
  840.     break;
  841.     case '3':
  842.     sprintf(out,"%03d",args[pcount++]);
  843.     out = &output[(int)strlen(output)];
  844.     break;
  845.     case '.':
  846.     *out++ = args[pcount++];
  847.     break;
  848.     case '+':
  849.     *out++ = args[pcount++] + *in++;
  850.     break;
  851.     case '>':
  852.     if (args[pcount] > *in++) {
  853.         args[pcount] += *in++;
  854.     } else {
  855.         in++;
  856.     }
  857.     break;
  858.     case 'r':
  859.     temp = args[pcount];
  860.     args[pcount] = args[pcount+1];
  861.     args[pcount+1] = temp;
  862.     break;
  863.     case 'i':
  864.     args[pcount]++;
  865.     args[pcount+1]++;
  866.     break;
  867.     case '%':
  868.     *out++ = '%';
  869.     break;
  870.     }
  871. }
  872.